<?php
/**
 * I-Cafe framework
 * 
 * @category CF
 * @package CF_Component
 * @link http://www.i-cafe.org
 * @license http://www.i-cafe.org/license
 * @version 04-04-2010
 * @author tcl_java@yahoo.com
 */

class CF_Component extends CF_Observable
{
	/**
	 * 
	 * @var array
	 */
	protected $_plugins = array();
	
	/**
	 * 
	 * @var array
	 */
	protected $_events = array(
		'beforeconstruct'		=> array(),
		'construct'				=> array(),
		'destruct'				=> array(),
		'beforeaddplugin'		=> array(),
		'addplugin'				=> array(),
		'beforeremoveplugin'	=> array(),
		'removeplugin'			=> array(),
		'beforecall'			=> array(),
		'call'					=> array()
	);
	
	/**
	 * 
	 * @param string|CF_Plugin_Abstract|array $plugin
	 * @return CF_Component $this
	 * @throws CF_Component_Exception
	 */
	public function addPlugins($plugin)
	{
		# notify before add plugin event
		if(CF_Observable::VALUE_RETURN === $this->fireEvent('beforeaddplugin', array($this, $plugin))) return;
		
		# if array of plugin
		if(is_array($plugin)){
			foreach ($plugin as $v){
				$this->addPlugins($v);
			}
			
			return $this;
		}
		
		# if $plugin is string - class name of plugin, create instance
		if(is_string($plugin)) $plugin = new $plugin;
		
		# plugin must be an instance of CF_Plugin_Abstract
		if(!$plugin instanceof CF_Plugin_Abstract){
			throw new CF_Component_Exception('Plugin must be an instance of CF_Plugin_Abstract');
		}
		
		# call init plugin
		$plugin->init($this);
		
		# add plugin to plugin array
		$this->_plugins[$plugin->__toString()] = $plugin;
		
		# notify add plugin event
		$this->fireEvent('addplugin', array($this, $plugin));
		
		return $this;
	}
	
	/**
	 * 
	 * @param string|CF_Plugin_Abstract|array $plugin
	 */
	public function removePlugins($plugin)
	{
		# notify before remove plugin
		if(CF_Observable::VALUE_RETURN === $this->fireEvent('beforeremoveplugin', array($this, $plugin))) return;
		
		# if array of plugin
		if(is_array($plugin)){
			foreach ($plugin as $v){
				$this->removePlugins($v);
			}
			
			return $this;
		}
		
		# get plugin name if it a instance of CF_Plugin_Abstract
		if($plugin instanceof CF_Plugin_Abstract)
			$plugin = $plugin->__toString();
		
		# unset from plugin array
		unset($this->_plugins[$plugin]);
		
		# notify remove plugin event
		$this->fireEvent('removeplugin', array($this, $plugin));
		
		return $this;
	}
	
	/**
	 * 
	 * @param string $name
	 * @param array $args
	 * @return mixed
	 * @throws CF_Component_Exception
	 */
	public function __call($name, $args)
	{
		# notify before call event
		if(CF_Observable::VALUE_RETURN === $this->fireEvent('beforecall', array($this, $name, $args))) return;
		
		$className = 'CF_Plugin_' . ucfirst($name);
		if(!isset($this->_plugins[$className]) || !is_callable(array($this->_plugins[$className], $name))){
			throw new CF_Component_Exception("Call indefined function $name via __call method");
		}
		
		# execute method
		$res = call_user_func_array(array($this->_plugins[$className], $name), $args);
		
		# nofify call event
		$this->fireEvent('call', array($this, &$res, &$name, $args));
		
		return $res;
	}
}
